package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {
    @Autowired
    private RestHighLevelClient restClient;
    @Override
    public PageResult search(RequestParams requestParams) {
//        获取搜索的条件key  页码和每页的数量
        try {
            SearchRequest searchRequest = new SearchRequest("hotel");
            Integer pageNo = requestParams.getPage();
            Integer pageSize = requestParams.getSize();
            String location = requestParams.getLocation();
            String sortBy = requestParams.getSortBy();
            BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
            buildBasicQuery(boolQuery,requestParams,searchRequest);
//            分页
            searchRequest.source().from((pageNo-1)*pageSize);
            searchRequest.source().size(pageSize);
            if (location!=null&&!location.trim().isEmpty()) {
//            排序  --按距离
                searchRequest.source().sort(SortBuilders
                        .geoDistanceSort("location", new GeoPoint(location))
                        .order(SortOrder.ASC)
                        .unit(DistanceUnit.KILOMETERS)
                );
            }
             if ("score".equals(sortBy)) {
//            排序  --按评分
                searchRequest.source().sort(SortBuilders.fieldSort("score").order(SortOrder.DESC));
            }
            if ("price".equals(sortBy)) {
//            排序  --按价格
                searchRequest.source().sort(SortBuilders.fieldSort("price").order(SortOrder.DESC));
            }

            SearchResponse searchResponse = restClient.search(searchRequest, RequestOptions.DEFAULT);
            return handlerResponse(searchResponse);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
//    多条件查询思路 用一个大的QueryBuilder比如booleanQueryBuilder 封装小的QueryBuilders 并最后query的时候用这个大的QueryBuilder
    private void buildBasicQuery(BoolQueryBuilder boolQuery,RequestParams requestParams,SearchRequest searchRequest){
        String key = requestParams.getKey();
        String brand = requestParams.getBrand();
        String city = requestParams.getCity();
        Integer maxPrice = requestParams.getMaxPrice();
        Integer minPrice = requestParams.getMinPrice();
        String starName = requestParams.getStarName();

//            查询条件
        if (key==null||key.trim().isEmpty()){
            boolQuery.must(QueryBuilders.matchAllQuery());
        }else {
            boolQuery.must(QueryBuilders.matchQuery("all", key));
        }
        if (brand!=null&&!brand.trim().isEmpty()){
            boolQuery.filter(QueryBuilders.termQuery("brand",brand));
        }
        if (city!=null&&!city.trim().isEmpty()){
            boolQuery.filter(QueryBuilders.termQuery("city", city));
        }
        if (starName!=null&&!starName.trim().isEmpty()){
            boolQuery.filter(QueryBuilders.termQuery("starName", starName));
        }
        if (maxPrice!=null&&minPrice!=null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(maxPrice).gte(minPrice));
        }
        //            算分控制
        FunctionScoreQueryBuilder functionScoreQueryBuilder =
                QueryBuilders.functionScoreQuery(
                    boolQuery
                    ,new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                    QueryBuilders.termQuery("isAD", true),
                                    ScoreFunctionBuilders.weightFactorFunction(10))
                        }
        );
        searchRequest.source().query(functionScoreQueryBuilder);
    }
    private PageResult handlerResponse(SearchResponse searchResponse){
        SearchHits hits = searchResponse.getHits();
        PageResult pageResult = new PageResult();
        ArrayList<HotelDoc> hotelDocs = new ArrayList<>();
        for (SearchHit hit : hits) {
            String objectJson = hit.getSourceAsString();
            HotelDoc hotelDoc = JSON.parseObject(objectJson, HotelDoc.class);
            Object[] sortValues = hit.getSortValues();
//            获取距离 封装到对象
            if (sortValues!=null&&sortValues.length>0){
                hotelDoc.setDistance(sortValues[0]);
            }


            hotelDocs.add(hotelDoc);
        }
        pageResult.setHotels(hotelDocs);
        pageResult.setTotal(hits.getTotalHits().value);
        return pageResult;
    }
}
